/*

Copyright (C) 2015-2018 Night Dive Studios, LLC.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
/*
 * $Source: r:/prj/cit/src/RCS/amaploop.c $
 * $Revision: 1.42 $
 * $Author: tjs $
 * $Date: 1994/11/20 18:02:29 $
 *
 * full screen map stuff
 */

#include <string.h>

#include "ShockBitmap.h"

#include "amaploop.h"
#include "audiolog.h"
#include "criterr.h"
#include "cybstrng.h"
#include "faketime.h"
#include "frflags.h"
#include "gamescr.h"
#include "gamestrn.h"
#include "gr2ss.h"
#include "lvldata.h"
#include "mainloop.h"
#include "mfdfunc.h"
#include "musicai.h"
#include "objgame.h"
#include "objsim.h"
#include "otrip.h"
#include "player.h"
#include "rcolors.h"
#include "wares.h"

// octant-wise, that is...
#define NORTH 0
#define EAST 2
#define SOUTH 4
#define WEST 6

// room for a message line on the bottom of the screen...

#define AMAP_BUTTON_WIDTH 92
#define AMAP_HEADER_HGT 11
#define AMAP_BORDER 4
#define AMAP_TOP(h) (AMAP_HEADER_HGT + AMAP_BORDER)
#define AMAP_LFT(w) (AMAP_BORDER)
#define AMAP_BOT(h) (h - 1 - AMAP_BORDER - AMAP_HEADER_HGT)
#define AMAP_RGT(w) (w - 1 - AMAP_BUTTON_WIDTH - AMAP_BORDER)
#define AMAP_HGT(h) (AMAP_BOT(h) - AMAP_TOP(h))
#define AMAP_WID(w) (AMAP_RGT(w) - AMAP_LFT(w))

#define FSMAP_MAX_MSG 40

#define MOUSE_UPS   (MOUSE_LUP | MOUSE_RUP | MOUSE_CUP)
#define MOUSE_DOWNS (MOUSE_LDOWN | MOUSE_RDOWN | MOUSE_CDOWN)

// spoofs for calling kb callback to do stuff
#define DO_ZOOMOUT KEY_PGUP
#define DO_ZOOMIN  KEY_PGDN
#define DO_RIGHT   KEY_RIGHT
#define DO_LEFT    KEY_LEFT
#define DO_UP      KEY_UP
#define DO_DOWN    KEY_DOWN
#define DO_QUIT    KEY_ESC
//#define DO_MSG     KEY_ENTER
#define DO_CHEAT    'f'
#define DO_SCAN     'r'
#define DO_CRITTER  'c'
#define DO_SECUR    's'
#define DO_RECENTER KEY_HOME
#define DO_FULLMSG  'm'

#define BTN_RECENTER 2
#define BTN_FULLMSG  3
#define BTN_CHEAT    6
#define BTN_SCAN     6
#define BTN_CRITTER  5
#define BTN_SECURE   4
#define BTN_ZOOMIN   0
#define BTN_ZOOMOUT  1
#define BTN_PANREG   8

#define BTN_NUM_TOT  9
#define BTN_NUM_REAL 7

#define BTN_TALK 0xdead // hack code for todo
#define BTN_PEND 0xbeef

// limit framerate while scrolling to avoid flicker?
// should really do something real about flicker anyway.
#define SCROLL_FRATE 90

frc *full_map_context;
#define FULLMAP_CANVAS ((grs_canvas *)fr_get_canvas(full_map_context))
// was full_game_fr_context

#define FSMAP_OPP 0x8000
// note this makes the init code 0b00010100000101, or 0x505
static ushort btn_to_code[] = {DO_ZOOMIN, DO_ZOOMOUT, DO_RECENTER, DO_FULLMSG, DO_SECUR, DO_CRITTER, DO_SCAN, 0};

static ushort btn_to_amap[] = {
    0, 0, FSMAP_OPP | AMAP_TRACK_OBJ, AMAP_FULL_MSG, AMAP_SHOW_SEC, AMAP_SHOW_CRIT | AMAP_SHOW_ROB, AMAP_SHOW_SENS, 0};

#define NUM_SIDE_BUTTONS 7

#define BOTTOM_BUTTONS_INDEX 7

// should alloc and dealloc these?, or take them out of some memory pool
static grs_canvas fsmap_actual, fsmap_bregion;
static ushort fsmap_buttons, fsmap_btn_pending;
static uchar bcolor[] = {GREEN_8_BASE + 7, GREEN_8_BASE + 3, GREEN_8_BASE, GREEN_8_BASE + 1};
static int cur_btn_hgt;
static char *cur_mapnote_base = NULL;
static char *cur_mapnote_ptr = NULL;
static uchar last_msg_ok = TRUE;
static ulong map_scrolltime = 0L;
static int map_scroll_d = 0;
static char map_scroll_code = 0;
static bool map_scroll_clicked = 0;

uchar pend_check(void);

// default units per second
// in defiance of Rob, I use the number 13.
#define MAP_SCROLL_SPEED 13

#define clear_cur_mapnote() cur_mapnote_base = cur_mapnote_ptr = NULL

// --------------------
//  INTERNAL PROTOTYPES
// --------------------
void trail_sp_punt(void);
void fsmap_button_redraw(void);
void fsmap_interface_draw(void);
void fsmap_message_redraw(void);
void fsmap_draw_map(void);
void fsmap_draw_screen(uint chng);
int s_bf(int btn_id, int todo);
void fsmap_new_msg(curAMap *amptr);
void edit_mapnote(curAMap *amptr);
uchar zoom_deal(curAMap *amptr, int btn);
uchar flags_deal(curAMap *amptr, int btn, int todo);
void btn_init(curAMap *amptr);

// The devil drives a Buick
// He sits inside and eats lunch
// Then he sticks his pitchfork through the trunk and into the spare
// and he pull out True Love

void fsmap_startup(void) {
    int i, n = 0, f, b, todo;
    grs_font *fsmap_font;

    // Do appropriate stuff to enter into amap mode here....
    automap_init(player_struct.hardwarez[HARDWARE_AUTOMAP], MFD_FULLSCR_MAP);
    f = oAMap(MFD_FULLSCR_MAP)->flags;
    oAMap(MFD_FULLSCR_MAP)->flags = 0;
    oAMap(MFD_FULLSCR_MAP)->zoom = 3; // KLC - added for 640x480 Mac fullscreen map
    for (i = 0; i < NUM_MFDS; i++) {
        if (oAMap(i)) {
            oAMap(MFD_FULLSCR_MAP)->flags |= oAMap(i)->flags;
            n++;
        }
    }
    if ((n == 0) || (oAMap(MFD_FULLSCR_MAP)->flags == 0))
        oAMap(MFD_FULLSCR_MAP)->flags = f;

    // Get the graphics system setup for fullscreen drawing.
    full_map_context =
        fr_place_view(FR_NEWVIEW, FR_DEFCAM, offscreenDrawSurface->pixels, FR_DOUBLEB_MASK | FR_WINDOWD_MASK, 0, 0, 0,
                      0, grd_screen_canvas->bm.w, grd_screen_canvas->bm.h);
    gr_set_canvas(FULLMAP_CANVAS);
    gr_clear(0xff);
    amap_pixratio_set(FIX_UNIT);
    fsmap_font = (grs_font *)ResLock(RES_largeTechFont); // KLC - was RES_mfdFont
    gr_set_font(fsmap_font);
    gr_init_sub_canvas(FULLMAP_CANVAS, &fsmap_actual, AMAP_LFT(grd_bm.w), AMAP_TOP(grd_bm.h), AMAP_WID(grd_bm.w),
                       AMAP_HGT(grd_bm.h));
    gr_init_sub_canvas(FULLMAP_CANVAS, &fsmap_bregion, AMAP_RGT(grd_bm.w) + AMAP_BORDER, AMAP_TOP(grd_bm.h),
                       AMAP_BUTTON_WIDTH - 2 * AMAP_BORDER, AMAP_HGT(grd_bm.h));
    fsmap_actual.gc.font = fsmap_font;
    fsmap_bregion.gc.font = fsmap_font;
    fsmap_buttons = (1 << (2 * BTN_ZOOMIN)) | (1 << (2 * BTN_ZOOMOUT));
    for (i = 0; i < NUM_SIDE_BUTTONS; i++) {
        b = btn_to_amap[i];
        if (b & oAMap(MFD_FULLSCR_MAP)->flags) {
            if (i == BTN_RECENTER)
                todo = AMAP_UNSET;
            else
                todo = AMAP_SET;
            s_bf(i, todo);
            pend_check();
        }
    }
    fsmap_btn_pending = 0;
    // KLC - seems to be causing a problem		btn_init(oAMap(MFD_FULLSCR_MAP));

    cur_btn_hgt = (((AMAP_HGT(grd_bm.h)) * 3) >> 5);

    chg_set_flg(LL_CHG_MASK);
}

void fsmap_free(void) {
    int i;
    for (i = 0; i < NUM_MFDS; i++) {
        oAMap(i)->flags = oAMap(MFD_FULLSCR_MAP)->flags;
        oAMap(i)->flags |= AMAP_TRACK_OBJ;
    }
    ResUnlock(RES_largeTechFont); // KLC - was RES_mfdFont
    gr_set_canvas(grd_screen_canvas);
}

void trail_sp_punt(void) {
    if (cur_mapnote_ptr == NULL)
        return;
    if (*cur_mapnote_ptr == '\0') {
        while (cur_mapnote_ptr > cur_mapnote_base)
            if ((*(cur_mapnote_ptr - 1)) != ' ')
                break;
            else
                cur_mapnote_ptr--;
        *cur_mapnote_ptr = '\0';
    }
}

#define BTN_HGT_MUL      (cur_btn_hgt)
#define GET_BTN_TOP(x)   ((x)*BTN_HGT_MUL)
#define GET_BTN_BOT(x)   (GET_BTN_TOP(x + 1) - AMAP_BORDER)
#define BUTTON_BUF_SIZE  10
#define AMAP_BUTTON_BASE REF_STR_AutomapButtons

void fsmap_button_redraw(void) {
    int i, cb, bsx, bsy, cx, cy;
    char button_buf[BUTTON_BUF_SIZE];

    gr_push_canvas(&fsmap_bregion);
    for (cb = fsmap_buttons, i = 0; i < NUM_SIDE_BUTTONS; i++, cb >>= 2) {
        gr_set_fcolor(bcolor[0]);
        ss_box(AMAP_BORDER, GET_BTN_TOP(i), grd_bm.w - AMAP_BORDER + 3, GET_BTN_BOT(i));
        gr_set_fcolor(bcolor[cb & 3]);
        ss_string(get_string(AMAP_BUTTON_BASE + i, button_buf, BUTTON_BUF_SIZE), 2 * AMAP_BORDER,
                  GET_BTN_TOP(i) + AMAP_BORDER + 10);
    }

    i = BOTTOM_BUTTONS_INDEX;

    gr_set_fcolor(bcolor[0]);

    // draw the pan controller... or have mini map...
    cx = AMAP_BORDER + 1;
    cy = GET_BTN_TOP(i);
    bsx = grd_bm.w - AMAP_BORDER + 3;
    bsy = grd_bm.h - BTN_HGT_MUL - AMAP_BORDER;

    ss_box(cx, cy, bsx, bsy);
    bsx -= cx;
    bsy -= cy;

    ss_int_line(cx, cy, cx + bsx - 1, cy + bsy - 1);
    ss_int_line(cx + bsx - 1, cy, cx, cy + bsy - 1);
    ss_string(get_temp_string(REF_STR_DirectionAbbrev + NORTH), cx + (bsx >> 1) - 3, cy + (bsy >> 2) - 3);
    ss_string(get_temp_string(REF_STR_DirectionAbbrev + EAST), cx + (bsx >> 1) + (bsx >> 2) - 2 + AMAP_BORDER,
              cy + (bsy >> 1) - 3);
    ss_string(get_temp_string(REF_STR_DirectionAbbrev + SOUTH), cx + (bsx >> 1) - 3, cy + bsy - (bsy >> 2) - 3);
    gr_string(get_temp_string(REF_STR_DirectionAbbrev + WEST), cx + (bsx >> 3) - 2 + AMAP_BORDER, cy + (bsy >> 1) - 3);

    // done button
    ss_box(AMAP_BORDER, grd_bm.h - BTN_HGT_MUL, grd_bm.w - AMAP_BORDER + 3, grd_bm.h);
    gr_set_fcolor(bcolor[cb & 3]);
    ss_string(get_string(AMAP_BUTTON_BASE + i, button_buf, BUTTON_BUF_SIZE), 2 * AMAP_BORDER,
              (grd_bm.h) - BTN_HGT_MUL + AMAP_BORDER + 12);

    gr_pop_canvas();
    chg_unset_flg(AMAP_BUTTON_EV);
}

char *fsmap_get_lev_str(char *buf, int siz) {
    int l;

    get_string(REF_STR_Level, buf, siz);
    l = strlen(buf);
    if (l + 3 < siz) {
        buf[l] = ' ';
        level_to_floor(player_struct.level, buf + l + 1);
    }
    return buf;
}

#define TRIOP_BUF_SIZE 50
#define TRIOP_STRING_BASE REF_STR_AutomapSpew
void fsmap_interface_draw(void) {
    char buf[TRIOP_BUF_SIZE];
    gr_set_fcolor(GREEN_8_BASE);
    ss_box(0, 0, grd_bm.w - 1, grd_bm.h - 1);
    ss_box(AMAP_LFT(grd_bm.w) - 1, AMAP_TOP(grd_bm.h) - 1, AMAP_RGT(grd_bm.w) + 1, AMAP_BOT(grd_bm.h) + 1);

    gr_set_fcolor(RED_8_BASE);
    ss_string(get_string(TRIOP_STRING_BASE, buf, TRIOP_BUF_SIZE), AMAP_LFT(grd_bm.w), AMAP_BORDER);

    ss_string(fsmap_get_lev_str(buf, TRIOP_BUF_SIZE), AMAP_RGT(grd_bm.w) + 2 * AMAP_BORDER, AMAP_BORDER);

    chg_unset_flg(AMAP_FULLEXPOSE);
}

#define MSG_BUF2_SIZE 25
void fsmap_message_redraw(void) {
    char buf[FSMAP_MAX_MSG];
    char buf2[MSG_BUF2_SIZE];
    short x, y, w, dummy;

    gr_set_font(fsmap_actual.gc.font); // so we dont need this as another global, ick
    gr_set_fcolor(0xFF);               // and someone keeps secretly reseting the font to ickiness
    ss_rect(AMAP_LFT(grd_bm.w), AMAP_BOT(grd_bm.h) + 1 + 2, grd_bm.w - 2, grd_bm.h - 2); // -2 to miss the border
    x = AMAP_LFT(grd_bm.w);
    y = AMAP_BOT(grd_bm.h) + 1 + 2;
    if (last_msg_ok) {
        if (cur_mapnote_base == NULL) {
            gr_set_fcolor(GREEN_8_BASE + 5);
        } else {
            gr_set_fcolor(RED_8_BASE + 2);
            strcpy(buf, "> ");
            gr_string_size(buf, &w, &dummy);
            ss_string(buf, x, y);
            ss_string(buf, x + 1, y);
            x += w + 1;
        }
        amap_get_note(oAMap(MFD_FULLSCR_MAP), buf);
        ss_string(buf, x, y); // +1 to get out of box, 2 for pad?
    } else {
        gr_set_fcolor(RED_8_BASE + 4);
        ss_string(get_string(REF_STR_NoMessage, buf2, MSG_BUF2_SIZE), AMAP_LFT(grd_bm.w), AMAP_BOT(grd_bm.h) + 1 + 2);
    }
    if (cur_mapnote_base != NULL) {
        gr_set_fcolor(PULSE_RED);
        ss_vline(x + gr_string_width(buf) + 2, AMAP_BOT(grd_bm.h) + 3, AMAP_BOT(grd_bm.h) + 8);
        ss_vline(x + gr_string_width(buf) + 2 + 1, AMAP_BOT(grd_bm.h) + 3, AMAP_BOT(grd_bm.h) + 8);
    }
    chg_unset_flg(AMAP_MESSAGE_EV);
}

void fsmap_draw_map(void) {
    FrameDesc *f;
    //   short w,h;

    //   w=res_bm_width(REF_IMG_bmTriLogoBack);
    //   h=res_bm_height(REF_IMG_bmTriLogoBack);
    gr_push_canvas(&fsmap_actual);
    gr_clear(0xff);

    // KLC - changed to draw the background logo double size
    //   draw_res_bm(REF_IMG_bmTriLogoBack,(grd_bm.w-w)/2,(grd_bm.h-h)/2);
    f = RefLock(REF_IMG_bmTriLogoBack);
    if (f == NULL)
        critical_error(CRITERR_MEM | 9);
    f->bm.bits = (uchar *)(f + 1);
    gr_scale_bitmap(&f->bm, (grd_bm.w - (f->bm.w * 2)) / 2, (grd_bm.h - (f->bm.h * 2)) / 2, f->bm.w * 2, f->bm.h * 2);
    RefUnlock(REF_IMG_bmTriLogoBack);

    amap_draw(oAMap(MFD_FULLSCR_MAP), 0);
    gr_pop_canvas();
    chg_unset_flg(AMAP_MAP_EV);
}

#define AMAP_ALLEVS (AMAP_FULLEXPOSE | AMAP_MAP_EV | AMAP_BUTTON_EV | AMAP_MESSAGE_EV)

void fsmap_draw_screen(uint chng) {
    int l, t, r, b;
    LGRect cr;

    gr_push_canvas(grd_screen_canvas);
    if ((chng & AMAP_ALLEVS) == AMAP_MAP_EV) {
        cr.ul.x = AMAP_LFT(grd_bm.w);
        cr.ul.y = AMAP_TOP(grd_bm.h);
        cr.lr.x = AMAP_LFT(grd_bm.w) + AMAP_WID(grd_bm.w);
        cr.lr.y = AMAP_TOP(grd_bm.h) + AMAP_HGT(grd_bm.h);
        gr_get_cliprect(&l, &t, &r, &b);
        ss_safe_set_cliprect(cr.ul.x, cr.ul.y, cr.lr.x, cr.lr.y);
        uiHideMouse(&cr);
        ss_bitmap(&(FULLMAP_CANVAS->bm), 0, 0);
        uiShowMouse(&cr);
        ss_safe_set_cliprect(l, t, r, b);
    } else {
        uiHideMouse(NULL);
        ss_bitmap(&(FULLMAP_CANVAS->bm), 0, 0);
        uiShowMouse(NULL);
    }
    gr_pop_canvas();
}

void automap_loop(void) {
    uint cf = _change_flag;

    // KLC - does nothing      loopLine(GL|0x1D,synchronous_update());
    if (music_on)
        loopLine(GL | 0x1C, mlimbs_do_ai());
    if (localChanges) {
        if (_change_flag & AMAP_FULLEXPOSE) {
            loopLine(AL | 0x1, fsmap_interface_draw());
        }
        if (_change_flag & AMAP_MAP_EV) {
            loopLine(AL | 0x2, fsmap_draw_map());
        }
        if (_change_flag & AMAP_BUTTON_EV) {
            loopLine(AL | 0x3, fsmap_button_redraw());
        }
        if (_change_flag & AMAP_MESSAGE_EV) {
            loopLine(AL | 0x4, fsmap_message_redraw());
        }
    }
    //   if (pal_fx_on) {
    //      loopLine(AL|0x41,palette_advance_all_fx(*tmd_ticks));
    //   }
    audiolog_loop_callback();
    if (cf & (AMAP_FULLEXPOSE | AMAP_MAP_EV | AMAP_BUTTON_EV | AMAP_MESSAGE_EV)) {
        fsmap_draw_screen(cf);
    }
}

int s_bf(int btn_id, int todo) {
    int prtlmask = 1 << (btn_id << 1);
    int fullmask = prtlmask + (prtlmask << 1);
    switch (todo) {
    case AMAP_TOGGLE:
        fsmap_buttons = (fsmap_buttons & ~fullmask) + ((fsmap_buttons & fullmask) ^ prtlmask);
        break; // xor on/off
    case AMAP_SET:
        fsmap_buttons = (fsmap_buttons & ~fullmask) + prtlmask;
        break; // set on/off to on
    case AMAP_UNSET:
        fsmap_buttons = (fsmap_buttons & ~fullmask);
        break; // set on/off to off
    case BTN_TALK:
        return fsmap_buttons & prtlmask; // return whether this is on or off
    case BTN_PEND:
        break;
    }
    fsmap_btn_pending |= (prtlmask << 1);
    fsmap_buttons |= fsmap_btn_pending;
    chg_set_flg(AMAP_BUTTON_EV);
    return fsmap_buttons & fullmask; // heck, why not
}

void fsmap_new_msg(curAMap *amptr) {
    cur_mapnote_base = amap_str_next();
    amap_note_value(amptr->note_obj) = amap_str_deref(cur_mapnote_base);
    cur_mapnote_ptr = cur_mapnote_base;
    *cur_mapnote_ptr = '\0';
    chg_set_flg(AMAP_MAP_EV);
}

uchar pend_check(void) {
    if (fsmap_btn_pending) {
        //      mprintf("UnPend %x (c %x)...",fsmap_btn_pending,fsmap_buttons);
        fsmap_buttons &= ~fsmap_btn_pending;
        fsmap_btn_pending = 0;
        chg_set_flg(AMAP_BUTTON_EV);
        //      mprintf("done %x\n",fsmap_buttons);
        return TRUE;
    }
    return FALSE;
}

#define hack_kb_callback(am, k) amap_kb_callback(am, k | KB_FLAG_DOWN)

#define UP_ARROW_CODE    0x7E
#define DOWN_ARROW_CODE  0x7D
#define LEFT_ARROW_CODE  0x7B
#define RIGHT_ARROW_CODE 0x7C

#define KP_UP_CODE       0x5B
#define KP_DOWN_CODE     0x54
#define KP_LEFT_CODE     0x56
#define KP_RIGHT_CODE    0x58

uchar amap_scroll_handler(uiEvent *ev, LGRegion *reg, intptr_t v) {
    int elapsed, now;
    short code;
    curAMap *amptr = oAMap(MFD_FULLSCR_MAP);

    if (!map_scroll_code || !map_scroll_clicked) {
        if (ev->type == UI_EVENT_KBD_POLL) {
            code = ev->raw_key_data.scancode;
            switch (code) {
            case UP_ARROW_CODE:
            case KP_UP_CODE:
                map_scroll_code = AMAP_PAN_N;
                break;
            case DOWN_ARROW_CODE:
            case KP_DOWN_CODE:
                map_scroll_code = AMAP_PAN_S;
                break;
            case LEFT_ARROW_CODE:
            case KP_LEFT_CODE:
                map_scroll_code = AMAP_PAN_W;
                break;
            case RIGHT_ARROW_CODE:
            case KP_RIGHT_CODE:
                map_scroll_code = AMAP_PAN_E;
                break;
            }
        }
    }

    if (map_scroll_code == 0)
        return FALSE;

    now = *tmd_ticks;
    elapsed = now - map_scrolltime;
    if (elapsed < (CIT_CYCLE / SCROLL_FRATE))
        return TRUE;
    if (elapsed > 10) elapsed = 10; //prevent jumping of map
    map_scrolltime = now;
    if (map_scroll_clicked) elapsed *= 10; //clicking nswe acts like a longer elapsed time
    map_scroll_d += (elapsed * MAP_SCROLL_SPEED * AMAP_DEF_DST) / CIT_CYCLE;
    amap_pan(amptr, map_scroll_code, &map_scroll_d);
    s_bf(BTN_RECENTER, AMAP_SET);
    pend_check();
    chg_set_flg(AMAP_MAP_EV);

    map_scroll_code = 0;
    map_scroll_clicked = FALSE;

    return TRUE;
}

void edit_mapnote(curAMap *amptr) {
    if ((cur_mapnote_ptr == NULL) && (amptr->note_obj)) {
        char buf[FSMAP_MAX_MSG];
        strcpy(buf, amap_note_string(amptr->note_obj));
        amap_str_delete(amap_note_string(amptr->note_obj));
        fsmap_new_msg(amptr);
        strcpy(cur_mapnote_base, buf);
        cur_mapnote_ptr = cur_mapnote_base + strlen(cur_mapnote_base);
        chg_set_flg(AMAP_MESSAGE_EV);
    }
}

uchar amap_ms_callback(curAMap *amptr, int x, int y, short action, ubyte b) {
    int scregion = 0;

    if (action & (MOUSE_WHEELUP | MOUSE_WHEELDN)) {
        if (zoom_deal(amptr, action & MOUSE_WHEELUP ? BTN_ZOOMIN : BTN_ZOOMOUT))
            chg_set_flg(AMAP_MAP_EV);
        pend_check();
        return TRUE;
    }

    if (action & MOUSE_UPS) {
        mouse_unconstrain();
        pend_check();
        return TRUE;
    }

    if (!(action & MOUSE_DOWNS))
        return FALSE;

    ui_mouse_constrain_xy(x, y, x, y);

    y -= AMAP_TOP(grd_bm.h);

    if (y > AMAP_HGT(grd_bm.h)) // if click in the message line
    {
        scregion = AMAP_MESSAGE_EV;
        edit_mapnote(amptr); // start editing.
    }

    else if (x > AMAP_RGT(grd_bm.w)) // If in the button regionÃ
    {
        if (y < GET_BTN_TOP(7) - AMAP_BORDER) // If in one of the upper buttons (above pan)
        {
            // KLC - changed to treat clicks and keyboard equivalents a little differently
            // 		  in the Mac version.  Doesn't call the hack_kb_callback anymore.

            int btn = y / BTN_HGT_MUL;
            char todo = AMAP_TOGGLE;

            if (btn == BTN_ZOOMIN || btn == BTN_ZOOMOUT) {
                if (zoom_deal(amptr, btn))
                    chg_set_flg(AMAP_MAP_EV);
            } else {
                if (btn == BTN_RECENTER)
                    todo = AMAP_UNSET;
                if (flags_deal(amptr, btn, todo))
                    chg_set_flg(AMAP_MAP_EV);
            }
        }

        else if (y > AMAP_HGT(grd_bm.h) - BTN_HGT_MUL) // If in the "Done" button
        {
            if (y < AMAP_HGT(grd_bm.h)) // quit automap... that was the done button kids.
                hack_kb_callback(amptr, DO_QUIT);
        }

        else // Else we must be in the pan region
        {
            x -= AMAP_RGT(grd_bm.w) + 2 * AMAP_BORDER + 1; // normalize to middle of pan region
            x -= 38;
            x *= 5;
            y -= GET_BTN_TOP(7);
            y -= 90;
            y *= 2;

            if ((abs(abs(x) - abs(y))) < 3)
                return TRUE;     // null pan area...
            if (abs(x) > abs(y)) // ew
                if (x > 0)
                    map_scroll_code = AMAP_PAN_E;
                else
                    map_scroll_code = AMAP_PAN_W;
            else if (y > 0)
                map_scroll_code = AMAP_PAN_S;
            else
                map_scroll_code = AMAP_PAN_N;
            map_scroll_clicked = TRUE;
        }
    } else {
        void *deal_data;
        ObjID prev_note;

        scregion = AMAP_MAP_EV;
        x -= AMAP_LFT(grd_bm.w);
        if ((x <= 0) || (y <= 0))
            return TRUE; // not really in map
        prev_note = amptr->note_obj;
	// will be NULL if clicked outside map
        if ((deal_data = amap_deal_with_map_click(amptr, &x, &y)) != NULL) {
            if (amptr->note_obj != prev_note) {
                trail_sp_punt();
                if (cur_mapnote_base != NULL) {
                    if (*cur_mapnote_base == '\0') {
                        obj_destroy(prev_note);
                    } else
                        amap_str_grab(cur_mapnote_base);
                }
                clear_cur_mapnote();
            }
            last_msg_ok = TRUE;
            chg_set_flg(AMAP_MESSAGE_EV | AMAP_MAP_EV);
            if (amptr->note_obj == OBJ_NULL) {
                trail_sp_punt();
                if ((amptr->note_obj = object_place(MAPNOTE_TRIPLE, MakePoint(x, y))) != OBJ_NULL)
                    fsmap_new_msg(amptr);
                else {
                    last_msg_ok = FALSE;
                    clear_cur_mapnote();
                    WARN("%s: No more mapnote space", __FUNCTION__);
                }
            }
            // if we want to do things when you click on old map notes...
            //         else
            //         {
            //            mprintf("Clicked on old map note\n");
            //         }
        } else {
            last_msg_ok = FALSE;
            chg_set_flg(AMAP_MESSAGE_EV);
        }
    }
    if (scregion != AMAP_MAP_EV && scregion != AMAP_MESSAGE_EV && amptr->note_obj != OBJ_NULL) {
        trail_sp_punt();
        if (cur_mapnote_base != NULL) {
            if (*cur_mapnote_base == '\0') {
                obj_destroy(amptr->note_obj);
                chg_set_flg(AMAP_MAP_EV);
            } else
                amap_str_grab(cur_mapnote_base);
        }
        clear_cur_mapnote();
        last_msg_ok = TRUE;
        chg_set_flg(AMAP_MESSAGE_EV);
    }
    return TRUE;
}

uchar zoom_deal(curAMap *amptr, int btn) {
    int zfac;
    uchar res;
    // now, set up for real
    if (btn == BTN_ZOOMIN)
        zfac = 1;
    else
        zfac = -1;
    res = amap_zoom(amptr, FALSE, zfac);
    if (res) {
        if (amptr->zoom + 1 >= AMAP_MAX_ZOOM)
            s_bf(BTN_ZOOMIN, AMAP_UNSET);
        else if (amptr->zoom <= AMAP_MIN_ZOOM)
            s_bf(BTN_ZOOMOUT, AMAP_UNSET);
        if ((btn == BTN_ZOOMIN) && (s_bf(BTN_ZOOMOUT, BTN_TALK) == 0))
            s_bf(BTN_ZOOMOUT, AMAP_SET);
        if ((btn == BTN_ZOOMOUT) && (s_bf(BTN_ZOOMIN, BTN_TALK) == 0))
            s_bf(BTN_ZOOMIN, AMAP_SET);
    }
    s_bf(btn, BTN_PEND);
    return res;
}

uchar flags_deal(curAMap *amptr, int btn, int todo) {
    short flgs = btn_to_amap[btn];
    int todo_bf = todo;
    uchar res;

    //   mprintf("Think deal with %x - %d\n",flgs,todo);
    if (flgs == 0)
        return FALSE;

    if (flgs & FSMAP_OPP) {
        switch (todo) {
        case AMAP_UNSET:
            todo_bf = AMAP_SET;
            break;
        case AMAP_SET:
            todo_bf = AMAP_UNSET;
            break;
        }
    }
    res = amap_flags(amptr, flgs, todo_bf);
    if (res)
        s_bf(btn, todo);
    return res;
}

void btn_init(curAMap *amptr) {
    int i, j;
    for (i = 3, j = (1 << (3 * 2)); i < BTN_NUM_REAL; i++, j <<= 2)
        if (fsmap_buttons & j) {
            if (btn_to_amap[i] != 0)
                if (!flags_deal(amptr, i, AMAP_SET))
                    fsmap_buttons &= ~j;
        } else
            flags_deal(amptr, i, AMAP_UNSET);
}

uchar amap_kb_callback(curAMap *amptr, int code) {
    //   char codewas;
    int exp = 0xff; // will get zeroed in case default for codes we ignore

    /*KLC - we're just forgetting most of the key equivalents for now

       if (code & KB_FLAG_DOWN)					//KLC - we'll process on key down.
       {
          codewas=map_scroll_code;
          map_scroll_code=0;
          if(pend_check()) return TRUE;
          if(!(codewas==0)) return TRUE;
       }
    */

    // If we're currently editing a message...

    if (cur_mapnote_ptr != NULL) {
        if (!(code & KB_FLAG_DOWN))
            return TRUE;
        code = kb2ascii(code);
        // KLC      if ((code==KEY_ENTER)||(code==KEY_DEL))
        if (code == KEY_ENTER) // If we've pressed Enter
        {
            if (code == KEY_ENTER) {
                trail_sp_punt();                  // clear out any trailing spaces
                if (amptr->flags & AMAP_FULL_MSG) // switch out of editing loop
                    chg_set_flg(AMAP_MAP_EV);
            }

            // KLC         if ((code==KEY_DEL)||(cur_mapnote_ptr==cur_mapnote_base))
            if (cur_mapnote_ptr == cur_mapnote_base) // If the map note string is empty
            {                                        // then delete the map note
                obj_destroy(amptr->note_obj);
                amptr->note_obj = 0;
                chg_set_flg(AMAP_MAP_EV);
            } else
                amap_str_grab(cur_mapnote_base);
            clear_cur_mapnote();
        } else if (isprint(code)) { // make sure it isnt too long
            int clen = strlen(cur_mapnote_base) + 1;

            if (amap_str_deref(cur_mapnote_base) + clen < AMAP_STRING_SIZE)
                if (clen < FSMAP_MAX_MSG) {
                    if (*cur_mapnote_ptr != '\0')
                        memcpy(cur_mapnote_ptr + 1, cur_mapnote_ptr, strlen(cur_mapnote_ptr));
                    else
                        *(cur_mapnote_ptr + 1) = '\0';
                    *cur_mapnote_ptr++ = (char)code;
                }
        } else if (code == KEY_BS) {
            if (cur_mapnote_ptr > cur_mapnote_base)
                *--cur_mapnote_ptr = '\0';
        } else
            return FALSE;
        chg_set_flg(AMAP_MESSAGE_EV);
        if (amptr->flags & AMAP_FULL_MSG)
            chg_set_flg(AMAP_MAP_EV);
        return TRUE;
    }

    // We're not editing.  Keyboard equivalents for buttons.

    else {
        char btn = -1, todo = AMAP_TOGGLE;
        map_scroll_code = 0;
        map_scroll_clicked = FALSE;
        switch (code & (~KB_FLAG_DOWN)) {
            // KLC      case KEY_PGUP:   case '[': if (!zoom_deal(amptr,BTN_ZOOMOUT)) exp=0; break;
            // KLC      case KEY_PGDN:   case ']': if (!zoom_deal(amptr,BTN_ZOOMIN))  exp=0; break;
            // KLC maybe put keyboard scrolling in later
            // KLC      case KEY_RIGHT:  case 'l': map_scroll_code=AMAP_PAN_E; break;
            // KLC      case KEY_LEFT:   case 'j': map_scroll_code=AMAP_PAN_W; break;
            // KLC      case KEY_UP:     case 'i': map_scroll_code=AMAP_PAN_N; break;
            // KLC      case KEY_DOWN:   case 'k': map_scroll_code=AMAP_PAN_S; break;
        case KEY_ESC:
        case 'q':
            _new_mode = _last_mode;
            chg_set_flg(GL_CHG_LOOP);
            break;
            // KLC      case KEY_BS:      edit_mapnote(amptr); break;
        case KEY_BS:
            obj_destroy(amptr->note_obj); // Delete the map note
            amptr->note_obj = 0;
            clear_cur_mapnote();
            last_msg_ok = TRUE;
            chg_set_flg(AMAP_MESSAGE_EV | AMAP_MAP_EV);
            break;
            // KLC      case DO_FULLMSG:           btn=BTN_FULLMSG; break;
            // KLC      case DO_RECENTER:          btn=BTN_RECENTER; todo=AMAP_UNSET; break;
            // KLC      case DO_SCAN:              btn=BTN_SCAN; break;
            // KLC      case DO_CRITTER:           btn=BTN_CRITTER; break;
            // KLC      case DO_SECUR:             btn=BTN_SECURE; break;
        default:
            exp = 0;
            break;
        }
        /* KLC - not used any more
              if (btn!=-1)
                 if (!flags_deal(amptr,btn,todo)) exp=0;
              if(map_scroll_code!=0) {
                 map_scrolltime=*tmd_ticks;
                 s_bf(BTN_RECENTER,AMAP_SET);
              }
        */
        if (exp) {
            chg_set_flg(AMAP_MAP_EV);
            return TRUE;
        }
        return FALSE;
    }
}
